package adfc2;

import adfc2.core.*;
import adfc2.entities.Building;
import adfc2.entities.BuildingPart;
import adfc2.entities.DroppedItem;
import adfc2.entities.Workshop;
import adfc2.items.Block;
import adfc2.items.Stone;
import adfc2.tasks.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.List;

import static adfc2.core.World.*;

/**
 * Created by IntelliJ IDEA.
 * User: aimozg
 * Date: 21.01.12
 * Time: 15:02
 */
public class MainFrame implements IRendererCallback {

    static enum MouseMode {
        SELECT, TARGET
    }

    static enum Order {
        MOVE("move"), PICKUP("pickup"), DIG("dig"), CONSTRUCT("construct"), CONSTRUCT_WALL("construct wall");
        public final String orderName;

        Order(String orderName) {
            this.orderName = orderName;
        }
    }

    MouseMode mode = MouseMode.SELECT;
    Order order = null;

    private final World world = new World();

    Creature selectedCreature;

    //----------------------------------------

    JFrame frame;
    Renderer renderer3D;
    private JPanel bottomPanel;
    private JLabel statusBar;
    private JPanel rightPanel;
    private JPanel rightInfoPanel;
    private JTextArea rightInfoText;
    private JTextArea rightInfoCommands;
    private JPanel leftPanel;

    private int hoverXY;

    public MainFrame() {
        world.mkblocks();
    }

    public void initGui() {
        //
        frame = new JFrame("Another Dwarf Fortress Clone");
        leftPanel = new JPanel();
        rightPanel = new JPanel();
        bottomPanel = new JPanel();
        statusBar = new JLabel();
        rightInfoPanel = new JPanel();
        rightInfoText = new JTextArea();
        rightInfoCommands = new JTextArea();

        ((FlowLayout) bottomPanel.getLayout()).setAlignment(FlowLayout.LEFT);
        bottomPanel.add(statusBar);

        rightInfoText.setFont(Font.decode("Courier New-12"));
        rightInfoCommands.setFont(rightInfoText.getFont());
        rightInfoPanel.setPreferredSize(new Dimension(300, 1));
        rightInfoPanel.setVisible(false);
        rightInfoPanel.setLayout(new BorderLayout());
        rightInfoPanel.add(rightInfoText, BorderLayout.CENTER);
        rightInfoPanel.add(rightInfoCommands, BorderLayout.SOUTH);
        rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.X_AXIS));
        rightPanel.add(rightInfoPanel);

        frame.setLayout(new BorderLayout());
        frame.add(leftPanel, BorderLayout.CENTER);
        frame.add(rightPanel, BorderLayout.EAST);
        frame.add(bottomPanel, BorderLayout.SOUTH);
        renderer3D = new Renderer3D(world, leftPanel, this);
        frame.setVisible(true);
        renderer3D.shown();
        frame.setSize(640, 480);
        frame.setExtendedState(Frame.MAXIMIZED_BOTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }


    private long lastTick = 0;


    public void loop() {
        while (true) {
            tick();
            showInfo();
            updateStatus();
            renderer3D.display();
        }
    }

    long avgdt = 1;

    private void tick() {
        long t = System.currentTimeMillis();
        long dt = (lastTick > 0) ? (t - lastTick) : 1;
        if (dt == 0) dt = 1;

        avgdt = (9 * avgdt + dt) / 10;
        frame.setTitle(String.format("Another Dwarf Fortress Clone. %d ms/tick [%.2f FPS]", avgdt, 1000.0 / avgdt));

        renderer3D.tick(dt);
        world.tick(dt);

        lastTick = t;
    }

    public void cellHover(int xy) {
        hoverXY = xy;
        updateStatus();
    }

    private void updateStatus() {
        int x = xy2x(hoverXY);
        int y = xy2y(hoverXY);
        if (hoverXY < 0 || hoverXY >= world.blocks.length) return;
        short block = world.blocks[hoverXY];

        List<Entity> entities = world.entitiesAt(hoverXY);

        String status = "";
        switch (mode) {
            case SELECT:
                status += "Select mode. ";
                break;
            case TARGET:
                status += "Target mode - " + order.orderName + ". ";
                break;
        }

        status += "[" + x + "," + y + "; ";
        //block
        status += blockName(block);
        if ((block & MASK_IMPASSABLE) != 0) status += ", impassable";
        if ((block & MASK_UNDERGROUND) != 0) status += ", underground";
        status += "]";
        //entities
        for (Entity entity : entities) {
            status += " " + entity.shortName();
        }
        statusBar.setText(status);
    }

    private String blockName(short block) {
        Material material = Material.get(block & MASK_MATERIAL);
        String s;
        short construction = (short) (block & MASK_CONSTRUCTION_ANY);
        if ((block & MASK_FILLED) != 0) {
            s = "solid " + material.shortName();
        } else if (construction != 0) {
            s = material.shortName() + " " + constructionName(construction);
        } else {
            s = material.shortName() + " floor";
        }
        return s;
    }

    public void cellClick(int xy, MouseEvent e) {
        int x = xy2x(xy);
        int y = xy2y(xy);
        short block = world.blocks[xy];
        List<Entity> entities = world.entitiesAt(xy);

        switch (e.getButton()) {
            case MouseEvent.BUTTON1:
                switch (mode) {
                    case SELECT: {
                        if (selectedCreature != null && selectedCreature.getXY() != xy) selectedCreature = null;
                        Creature firstCreature = null;
                        boolean wasSel = false;
                        for (Entity entity : entities) {
                            if (entity instanceof Creature) {
                                if (selectedCreature == null) {
                                    selectedCreature = (Creature) entity;
                                    break;
                                } else {
                                    if (firstCreature == null) firstCreature = (Creature) entity;
                                    if (wasSel) {
                                        selectedCreature = (Creature) entity;
                                        firstCreature = null;
                                        break;
                                    }
                                    if (entity == selectedCreature) {
                                        wasSel = true;
                                    }
                                }
                            }
                        }
                        if (firstCreature != null) selectedCreature = firstCreature;
                    }
                    break;
                    case TARGET:
                        if (selectedCreature != null) {
                            switch (order) {
                                case MOVE:
                                    new MoveTask(selectedCreature, xy, false).start();
                                    cancelTarget();
                                    return;
                                case DIG:
                                    new DigTask(selectedCreature, xy).start();
                                    cancelTarget();
                                    return;
                                case PICKUP:
                                    for (Entity entity : entities) {
                                        if (entity instanceof DroppedItem) {
                                            new PickupTask(selectedCreature, (DroppedItem) entity).start();
                                            cancelTarget();
                                            return;
                                        }
                                    }
                                    break;
                                case CONSTRUCT:
                                    // do nothing
                                    break;
                                case CONSTRUCT_WALL:
                                    Item material = selectedCreature.itemLike(new Block(Material.GRANITE), Item.ICMASK_TYPE);
                                    if (material == null)
                                        material = selectedCreature.itemLike(new Stone(Material.GRANITE), Item.ICMASK_TYPE);
                                    if (material == null) break;
                                    new ConstructTask(selectedCreature, World.MASK_CONSTRUCTION_WALL, xy, material).start();
                                    break;
                                default:
                                    throw new UnsupportedOperationException(order + " unknown order");
                            }
                        }
                }
                break;
            case MouseEvent.BUTTON3:
                switch (mode) {
                    case TARGET:
                        break;
                    case SELECT:
                        if (selectedCreature != null) {
                            smartAction(selectedCreature, xy);
                        }
                        break;
                    default:
                        throw new UnsupportedOperationException(mode + " unknown mode");
                }
        }

    }

    private void smartAction(Creature creature, int xy) {
        short block = world.blocks[xy];
        List<Entity> entities = world.entitiesAt(xy);
        for (Entity entity : entities) {
            if (entity instanceof Building || entity instanceof BuildingPart) {
                Building b = (entity instanceof Building) ? (Building) entity : ((BuildingPart) entity).building;
                if (b instanceof Workshop) {
                    Workshop w = (Workshop) b;
                    if (w.worker != creature) {
                        new EnterWorkshopTask(creature, w).start();
                        return;
                    }
                }
            } else if (entity instanceof DroppedItem) {
                new PickupTask(creature, (DroppedItem) entity).start();
                return;
            }
        }
        if ((block & World.MASK_FILLED) != 0) {
            new DigTask(creature, xy).start();
            return;
        }
        if (passable(block)) {
            new MoveTask(creature, xy, false).start();
            return;
        }
    }

    private void showInfo() {
        StringBuilder infotext = new StringBuilder();
        if (selectedCreature == null) {
            rightInfoPanel.setVisible(false);
            rightInfoCommands.setText("");
        } else {
            rightInfoPanel.setVisible(true);
            infotext.append("Selected: ").append(selectedCreature.shortName()).append("\n");
            if (selectedCreature.container == null) {
                infotext.append("\n");
            } else {
                infotext.append("In the ").append(selectedCreature.container.shortName()).append("\n");
            }
            infotext.append("Task: ");
            Task task = selectedCreature.task;
            if (task == null) {
                infotext.append("Idle");
            } else {
                infotext.append(task.shortName());
            }
            infotext.append("\n\n");
            List<Item> items = selectedCreature.backpack();
            if (items.isEmpty()) {
                infotext.append(selectedCreature.gender().pronoun).append(" posseses nothing.\n");
            } else {
                infotext.append(selectedCreature.gender().pronoun).append(" has:\n");
                Iterator<Item> ii = items.iterator();
                infotext.append(ii.next().shortName());
                while (ii.hasNext()) {
                    infotext.append(", ").append(ii.next().shortName());
                }
                infotext.append("\n");
            }
            // TODO if controlable
            StringBuilder cmdtext = new StringBuilder();
            cmdtext.append("Commands:\n");
            if (order == null) {
                cmdtext.append(" [m] Move\n");
                cmdtext.append(" [p] Pickup\n");
                cmdtext.append(" [d] Dig\n");
                cmdtext.append(" [C] Construct\n");
            } else {
                switch (order) {
                    case CONSTRUCT:
                        cmdtext.append(" Construct ...\n");
                        cmdtext.append(" [w] Wall\n");
                        break;
                    default:
                        cmdtext.append(" [ESC] Cancel\n");
                        break;
                }
            }
            rightInfoCommands.setText(cmdtext.toString());
        }
        rightInfoText.setText(infotext.toString());
    }

    public void keyEvent(KeyEvent e) {
        char ch = e.getKeyChar();
        switch (mode) {
            case SELECT:
                if (selectedCreature != null) { // TODO and controlable
                    switch (ch) {
                        case 'm':
                            mode = MouseMode.TARGET;
                            order = Order.MOVE;
                            return;
                        case 'd':
                            mode = MouseMode.TARGET;
                            order = Order.DIG;
                            return;
                        case 'p':
                            mode = MouseMode.TARGET;
                            order = Order.PICKUP;
                            return;
                        case 'C':
                            mode = MouseMode.TARGET;
                            order = Order.CONSTRUCT;
                            return;
                    }
                }
                switch (ch) {
                    case KeyEvent.VK_ESCAPE:
                        selectedCreature = null;
                        break;
                }
                break;
            case TARGET:
                if (order == Order.CONSTRUCT) {
                    if (ch == 'w') {
                        order = Order.CONSTRUCT_WALL;
                        return;
                    }
                }
                switch (ch) {
                    case KeyEvent.VK_ESCAPE:
                        cancelTarget();
                        return;
                }
                break;
        }
    }

    private void cancelTarget() {
        order = null;
        mode = MouseMode.SELECT;
    }
}
